package cn.qingyun.gis.modules.express.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.qingyun.gis.modules.caronline.entity.dto.CarExportTotalDto;
import cn.qingyun.gis.modules.caronline.mapper.CarProofreadMapper;
import cn.qingyun.gis.modules.express.constant.ExpressStatus;
import cn.qingyun.gis.modules.express.entity.*;
import cn.qingyun.gis.modules.express.entity.dto.ExportExpressProofreadDto;
import cn.qingyun.gis.modules.express.mapper.ExpressInfoMapper;
import cn.qingyun.gis.modules.express.mapper.ExpressInfoTotalMapper;
import cn.qingyun.gis.modules.express.mapper.ExpressProofreadMapper;
import cn.qingyun.gis.modules.express.service.*;
import cn.qingyun.gis.modules.express.util.SyncTool;
import cn.qingyun.gis.modules.init.system.entity.SysUser;
import cn.qingyun.gis.modules.init.system.mapper.SysUserMapper;
import cn.qingyun.gis.modules.init.utils.CsvTool;
import cn.qingyun.gis.modules.init.utils.JwtUtil;
import cn.qingyun.gis.modules.takeaway.entity.dto.TakeawayExportTotalDto;
import cn.qingyun.gis.modules.takeaway.mapper.TakeawayInfoMapper;
import cn.qingyun.gis.modules.takeaway.mapper.TakeawayProofreadMapper;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ExpressDataInitServiceImpl implements IExpressDataInitService {
    @Resource
    private IExpressProofreadService iExpressProofreadService;
    @Resource
    private IExpressAttachmentService iExpressAttachmentService;
    @Resource
    private IExpressInfoService iExpressInfoService;

    @Resource
    private IExpressRegionService iExpressRegionService;

    @Resource
    private IExpressRegionHandleService iExpressRegionHandleService;

    @Resource
    private IExpressProofreadHandleService iExpressProofreadHandleService;

    @Resource
    private ExpressProofreadMapper expressProofreadMapper;

    @Resource
    private CarProofreadMapper carProofreadMapper;

    @Resource
    private TakeawayProofreadMapper takeawayProofreadMapper;

    @Resource
    private TakeawayInfoMapper takeawayInfoMapper;

    @Resource
    private ExpressInfoTotalMapper expressInfoTotalMapper;
    @Resource
    private ExpressInfoMapper expressInfoMapper;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private HttpServletResponse response;


    @Override
    public void initExpressExcel20230727(MultipartFile file) {
        log.debug("开始初始化");
        new Thread(() -> {
            try {
                List<ExpressProofread> addExpressDatas = Lists.newArrayList();
                EasyExcelFactory.read(file.getInputStream(), initExpressExcel20230727.class, new ReadListener<initExpressExcel20230727>() {
                    @Override
                    public void onException(Exception exception, AnalysisContext context) {
                        if (exception instanceof ExcelDataConvertException) {
                            log.error("解析异常, 行：{}", ((ExcelDataConvertException) exception).getRowIndex());
                        } else {
                            log.error("解析异常, {}", exception.getClass().getName());
                        }
                    }

                    @Override
                    public void invoke(initExpressExcel20230727 data, AnalysisContext context) {

                        ExpressProofread expressProofread = new ExpressProofread();
                        BeanUtil.copyProperties(data, expressProofread);
                        expressProofread.setGridEncoding(data.getGridId());
                        expressProofread.setCreateTime(DateUtil.date());
                        expressProofread.setUpdateTime(DateUtil.date());
                        expressProofread.setKwStatus(ExpressStatus.UN_PROOFED);
                        addExpressDatas.add(expressProofread);

                        if (addExpressDatas.size() == 100) {
                            iExpressProofreadService.saveBatch(addExpressDatas);
                            addExpressDatas.clear();
                        }
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        iExpressProofreadService.saveBatch(addExpressDatas);
                        log.debug("导入完成");
                    }
                }).sheet().doReadSync();

            } catch (IOException e) {
                log.error("导入失败", e);
            }
        }).start();
    }

    @Override
    public void importExcel20230727(MultipartFile file) {
        log.debug("开始导入");
        new Thread(() -> {
            try {
                List<ExpressProofread> addExpressDatas = Lists.newArrayList();
                EasyExcelFactory.read(file.getInputStream(), ImportExpressExcel20230727.class, new ReadListener<ImportExpressExcel20230727>() {
                    @Override
                    public void onException(Exception exception, AnalysisContext context) {
                        if (exception instanceof ExcelDataConvertException) {
                            log.error("解析异常, 行：{}", ((ExcelDataConvertException) exception).getRowIndex());
                        } else {
                            log.error("解析异常, {}", exception.getClass().getName());
                        }
                    }

                    @Override
                    public void invoke(ImportExpressExcel20230727 data, AnalysisContext context) {

                        ExpressProofread expressProofread = new ExpressProofread();
                        BeanUtil.copyProperties(data, expressProofread);
                        expressProofread.setGridEncoding(data.getGridId());
                        addExpressDatas.add(expressProofread);

                        if (addExpressDatas.size() == 100) {
                            iExpressProofreadService.saveBatch(addExpressDatas);
                            addExpressDatas.clear();
                        }
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        iExpressProofreadService.saveBatch(addExpressDatas);
                        log.debug("导入完成");
                    }
                }).sheet().doReadSync();

            } catch (IOException e) {
                log.error("导入失败", e);
            }
        }).start();
    }

    @Override
    @Transactional
    public void formattingExpressExcel() {
        //格式化主表与副表
        iExpressProofreadService.remove(new QueryWrapper<>());
        iExpressInfoService.remove(new QueryWrapper<>());
        iExpressAttachmentService.remove(new QueryWrapper<>());
    }

    @SneakyThrows
    @Override
    public void exportExpressExcel() {
        response.setContentType("application/vnd.ms-excel");
        String fileName = "垂类地址信息匹配结果表";
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));
        // 根据分页进行多线程并发导入
        int count = iExpressProofreadService.count();
        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            // 此处使用导入实体类充当导出
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "全量数据").head(ImportExpressExcel20230727.class).build();
            int pages = (count / 1000) + 1;
            //并发导入
            SyncTool.doSync(pages, i -> {
                Page<ImportExpressExcel20230727> page = new Page<>(i, 1000);
                List<ImportExpressExcel20230727> expressProofreads = iExpressProofreadService.exportExpressExcelBatch(page).getRecords();
                synchronized (excelWriter) {
                    excelWriter.write(expressProofreads, writeSheet);
                }
            });
        }
        log.debug("已成功导出{}条垂类地址数据", count);
    }

    @SneakyThrows
    @Override
    public void exportExpressProofreadExcel() {
        response.setContentType("application/vnd.ms-excel");
        String fileName = "垂类地址信息明细结果表";
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));
        // 根据分页进行多线程并发导入
        int count = expressInfoMapper.selectCount(new QueryWrapper<>());
        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            // 此处使用导入实体类充当导出
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "全量数据").head(ExportExpressProofreadDto.class).build();
            int pages = (count / 1000) + 1;
            //并发导出
            SyncTool.doSync(pages, i -> {
                Page<ExportExpressProofreadDto> page = new Page<>(i, 1000);
                IPage<ExportExpressProofreadDto> expressInfoTotal = expressInfoTotalMapper.selectExpressProofreadTotal(page, ExpressStatus.PROOFED, null);
                synchronized (excelWriter) {
                    excelWriter.write(expressInfoTotal.getRecords(), writeSheet);
                }
            });
        }
        log.debug("已成功导出{}条垂类数据", count);
    }

    @SneakyThrows
    @Override
    public void exportExpressDetail(HttpServletRequest request) {
        response.setContentType("application/vnd.ms-excel");
        String fileName = "垂类地址信息明细表";
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));
        // 获取当前用户信息
        String currentUserName = JwtUtil.getUserNameByToken(request);

        // 根据用户信息查询区域编码
        SysUser user = sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().
                select(SysUser::getRegionCode).
                eq(SysUser::getUsername, currentUserName));
        log.debug("当前登录用户信息：{} 以及相应配置地市权限编码{}", currentUserName, user.getRegionCode());
        String regionCode;
        if ("0000SD".equals(user.getRegionCode())) { // 当前省级配置工号
            regionCode = null;
        } else {
            regionCode = user.getRegionCode();
        }

        // 根据分页进行多线程并发导入
        int expressCount = expressProofreadMapper.selectCount(new QueryWrapper<>());
        int carCount = carProofreadMapper.selectCount(null);
        int takeAwayCount = takeawayProofreadMapper.selectCount(null);
        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            // 快递明细
            // 此处使用导入实体类充当导出
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "快递全量数据").head(ExportExpressProofreadDto.class).build();
            int expressPages = (expressCount / 1000) + 1;
            //并发导出
            SyncTool.doSync(expressPages, i -> {
                Page<ExportExpressProofreadDto> page = new Page<>(i, 1000);
                List<ExportExpressProofreadDto> infoTotals = expressInfoTotalMapper.selectExpressProofreadTotal(page, null, regionCode).getRecords();
                infoTotals.forEach(info -> {
                    info.setType("快递");
                });
                synchronized (excelWriter) {
                    excelWriter.write(infoTotals, writeSheet);
                }
            });

            // 网约车明细
            WriteSheet carSheet = EasyExcelFactory.writerSheet(1, "网约车全量数据").head(CarExportTotalDto.class).build();
            int carPages = (carCount / 1000) + 1;
            //并发导出
            SyncTool.doSync(carPages, i -> {
                Page<CarExportTotalDto> page = new Page<>(i, 1000);
                List<CarExportTotalDto> infoTotals = carProofreadMapper.selectCarTotal(page, null, regionCode).getRecords();
                infoTotals.forEach(info -> {
                    info.setType("网约车");
                });
                synchronized (excelWriter) {
                    excelWriter.write(infoTotals, carSheet);
                }
            });

            // 外卖明细
            WriteSheet takeAwaySheet = EasyExcelFactory.writerSheet(2, "外卖全量数据").head(TakeawayExportTotalDto.class).build();
            int takeAwayPages = (takeAwayCount / 1000) + 1;
            //并发导出
            SyncTool.doSync(takeAwayPages, i -> {
                Page<TakeawayExportTotalDto> page = new Page<>(i, 1000);
                List<TakeawayExportTotalDto> infoTotals = takeawayInfoMapper.selectTakeawayTotal(page, null, regionCode).getRecords();
                infoTotals.forEach(info -> {
                    info.setType("外卖");
                });
                synchronized (excelWriter) {
                    excelWriter.write(infoTotals, takeAwaySheet);
                }
            });
        }
        log.debug("已成功导出{}条垂类地址全量数据 | 已成功导出{}条网约车地址全量数据 | 已成功导出{}条外卖地址全量数据", expressCount, carCount, takeAwayCount);
    }

    @Override
    public void importBusinessGridCsv(MultipartFile file) {
        try {
            CsvReader csvReader = new CsvReader(file.getInputStream(), StandardCharsets.UTF_8);
            // 读表头
            csvReader.setSafetySwitch(false);
            csvReader.readHeaders();
            List<ImportBusinessGridCsv> imports = Lists.newArrayList();
            // 读内容
            while (csvReader.readRecord()) {
                imports.add(CsvTool.toBean(csvReader, ImportBusinessGridCsv.class));
            }

            if (CollUtil.isNotEmpty(imports)) {
                int batchSize = 1000; //批次
                List<ExpressRegion> recordsBatch = Lists.newArrayList(); //存放批次数据
                int i = 0;
                for (ImportBusinessGridCsv anImport : imports) {
                    ExpressRegion expressRegion = new ExpressRegion();
                    BeanUtils.copyProperties(anImport, expressRegion);
                    expressRegion.setCreateTime(DateUtil.date());
                    expressRegion.setUpdateTime(DateUtil.date());
                    recordsBatch.add(expressRegion);

                    //分批添加到数据库中
                    if ((i + 1) % batchSize == 0 || (i + 1) == imports.size()) {
                        iExpressRegionService.saveBatch(recordsBatch);
                        recordsBatch.clear();
                    }
                    i++;
                }
                log.debug("数据解析成功");
            }
        } catch (Exception e) {
            log.error("数据解析失败", e);
        }
    }

    @Override
    public void formattingBusinessGridCsv() {
        iExpressRegionService.remove(new QueryWrapper<>());
    }

    @SneakyThrows
    @Override
    public void downloadRegionCsv() {
        response.setContentType("application/csv; charset=utf-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + new String("业务区划数据集.csv".getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
        CsvWriter csvWriter = new CsvWriter(response.getOutputStream(), ',', StandardCharsets.UTF_8);
        String[] fields = {"code", "parent_code", "name", "level", "center_point", "geometry"};
        String[] types = {"string", "string", "string", "string", "string"};
        csvWriter.writeRecord(fields);
        csvWriter.writeRecord(types);
        List<ExpressRegion> districts = iExpressRegionService.list();

        QueryWrapper<ExpressRegion> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().inSql(ExpressRegion::getCode, "select code from express_region");
        List<ExpressRegion> wkts = iExpressRegionService.list(queryWrapper);
        Map<String, ExpressRegion> wktMap = wkts.stream().collect(Collectors.toMap(ExpressRegion::getCode, item -> item));

        for (ExpressRegion district : districts) {
            ExpressRegion businessWkt = wktMap.get(district.getCode());
            if (Objects.nonNull(businessWkt)) {
                String[] values = {district.getCode(), district.getParentCode(), district.getName(), district.getLevel(), businessWkt.getCenterPoint(), businessWkt.getGeometry()};
                csvWriter.writeRecord(values);
            }
        }
        log.debug("已成功导出{}条业务规划数据", iExpressRegionHandleService.count());
        csvWriter.close();
    }

    @Override
    public void importGridNewCsv(MultipartFile file) {
        try {
            CsvReader csvReader = new CsvReader(file.getInputStream(), StandardCharsets.UTF_8);
            // 读表头
            csvReader.setSafetySwitch(false);
            csvReader.readHeaders();
            List<ImportGridNewCsv> imports = Lists.newArrayList();
            // 读内容
            while (csvReader.readRecord()) {
                imports.add(CsvTool.toBean(csvReader, ImportGridNewCsv.class));
            }

            if (CollUtil.isNotEmpty(imports)) {
                int batchSize = 100; //批次
                List<ExpressRegionHandle> recordsBatch = Lists.newArrayList(); //存放批次数据
                int i = 0;
                for (ImportGridNewCsv anImport : imports) {
                    //if(!"5".equals(anImport.getLevel())){
                    ExpressRegionHandle expressRegionHandle = new ExpressRegionHandle();
                    BeanUtils.copyProperties(anImport, expressRegionHandle);
                    expressRegionHandle.setGeometry84(anImport.getGeometry84().replace("\"", ""));
                    //时间数据导入
                    expressRegionHandle.setCreateTime(DateUtil.date());
                    expressRegionHandle.setUpdateTime(DateUtil.date());
                    recordsBatch.add(expressRegionHandle);
                    //分批添加到数据库中
                    if ((i + 1) % batchSize == 0 || (i + 1) == imports.size()) {
                        iExpressRegionHandleService.saveBatch(recordsBatch);
                        recordsBatch.clear();
                    }
                    i++;
                    //}
                }
                log.debug("数据解析成功");
            }
        } catch (Exception e) {
            log.error("数据解析失败", e);
        }
    }

    @Override
    public void formattingGridNewCsv() {
        iExpressRegionHandleService.remove(new QueryWrapper<>());
    }

    @Override
    public void importHandleExpressExcel20230727(MultipartFile file) {
        log.debug("开始导入");
        new Thread(() -> {
            try {
                List<ExpressProofreadHandle> addExpressDatas = Lists.newArrayList();
                EasyExcelFactory.read(file.getInputStream(), ImportExpressExcel20230727.class, new ReadListener<ImportExpressExcel20230727>() {
                    @Override
                    public void onException(Exception exception, AnalysisContext context) {
                        if (exception instanceof ExcelDataConvertException) {
                            log.error("解析异常, 行：{}", ((ExcelDataConvertException) exception).getRowIndex());
                        } else {
                            log.error("解析异常, {}", exception.getClass().getName());
                        }
                    }

                    @Override
                    public void invoke(ImportExpressExcel20230727 data, AnalysisContext context) {

                        ExpressProofreadHandle expressProofreadHandle = new ExpressProofreadHandle();
                        BeanUtil.copyProperties(data, expressProofreadHandle);
                        expressProofreadHandle.setGridEncoding(data.getGridId());
                        expressProofreadHandle.setMatchStatus(ExpressStatus.UN_MATCH);
                        expressProofreadHandle.setSuccessStatus(ExpressStatus.UN_SUCCESS_MATCH);
                        expressProofreadHandle.setCreateTime(DateUtil.date());
                        expressProofreadHandle.setUpdateTime(DateUtil.date());
                        addExpressDatas.add(expressProofreadHandle);

                        if (addExpressDatas.size() == 10) {
                            iExpressProofreadHandleService.saveBatch(addExpressDatas);
                            addExpressDatas.clear();
                        }
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        iExpressProofreadHandleService.saveBatch(addExpressDatas);
                        log.debug("导入完成");
                    }
                }).sheet().doReadSync();

            } catch (IOException e) {
                log.error("导入失败", e);
            }
        }).start();
    }

    @Override
    public void formattingHandleExpressExcel() {
        iExpressProofreadHandleService.remove(new QueryWrapper<>());
    }


    @SneakyThrows
    @Override
    public void exportHandleExpressExcel(HttpServletResponse response) {

        response.setContentType("application/vnd.ms-excel");
        String fileName = "垂类地址信息匹配结果表0727表（处理）";
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));
        // 根据分页进行多线程并发导出
        int count = iExpressProofreadHandleService.count();
        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "全量数据").head(ExportHandleExpressExcel20230727.class).build();
            int pages = (count / 1000) + 1;
            //并发导出
            SyncTool.doSync(pages, i -> {
                Page<ExportHandleExpressExcel20230727> page = new Page<>(i, 1000);
                List<ExportHandleExpressExcel20230727> expressProofreadHandles = iExpressProofreadHandleService.exportExpressHandleExcelBatch(page).getRecords();
                synchronized (excelWriter) {
                    excelWriter.write(expressProofreadHandles, writeSheet);
                }
            });
        }
        log.debug("已成功导出{}条处理数据", count);
    }
}
